package ru.vsu.amm.fuzzy.problems;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import ru.vsu.amm.algebra.Matrix;
import ru.vsu.amm.fuzzy.Convolutions;
import ru.vsu.amm.io.MatrixIO;

/**
 * @author Иванов Илья
 * @since 03 января 2013
 *
 */
public class StudentsRange {

	private static Matrix data;
	private static List<String> labels;
	private static Matrix pComps;
	private static BufferedWriter writer;
	
	private static final double EIGEN_VECTOR_PRECISION = 1E-10;
	private static final double QUANTIFICATION_ALPHA = 0.25;
	
	private static final Comparator<Object[]> CONVOLUTIONS_DESCENDING_COMPARATOR = new Comparator<Object[]>() {
		public int compare(Object[] o1, Object[] o2) {
			return - ((Double) o1[1]).compareTo((Double) o2[1]);
		}
	};
	
	public static void main(String[] args) {
		if (args.length < 3)
			return;
		
		try {
			data = MatrixIO.readFromFile(args[0]);
			labels = Utils.readLabels(args[0]);
			pComps = MatrixIO.readFromFile(args[1]);
			if (labels.size() != data.rows() || !pComps.isSquare() || pComps.rows() != data.columns()) {
				System.out.println("Некорректные входные данные");
				return;
			}
			
			writer = new BufferedWriter(new FileWriter(args[2]));
			
			Object[][] convolutions = calcAdditiveConvolutions();
			Arrays.sort(convolutions, CONVOLUTIONS_DESCENDING_COMPARATOR);
			normalizeConvolutions(convolutions);
			outputConvolutions(convolutions, "Ранжирование по невозрастанию относительных аддитивных свёрток:");
			
			convolutions = calcOWAConvolutions();
			Arrays.sort(convolutions, CONVOLUTIONS_DESCENDING_COMPARATOR);
			outputConvolutions(convolutions, "Ранжирование по невозрастанию значений OWA-оператора");
			
			writer.close();
		} catch(IOException e) {
			e.printStackTrace();
			return;
		}
	}
	
	private static Object[][] calcAdditiveConvolutions() throws IOException {
		double[] weights = pComps.maxEigenvector(EIGEN_VECTOR_PRECISION).getColumn(0);
		writer.write("Вектор весов, полученный методом парных сравнений:");
		writer.newLine();
		for (double w : weights) {
			writer.write(String.valueOf(w));
			writer.newLine();
		}
		writer.newLine();
		
		Object[][] convolutions = new Object[data.rows()][];
		for (int i = 0; i < convolutions.length; i++)
			convolutions[i] = new Object[] {
				labels.get(i),
				Convolutions.additive(weights, data.getRow(i))
			};
		
		return convolutions;
	}
	
	private static Object[][] calcOWAConvolutions() throws IOException {
		double[] weights = getWeightVector(data.columns(), QUANTIFICATION_ALPHA);
		writer.write("Вектор весов, полученный на основании функции квантификации Q(x) = x ^ " + QUANTIFICATION_ALPHA + ":");
		writer.newLine();
		for (double w : weights) {
			writer.write(String.valueOf(w));
			writer.newLine();
		}
		writer.newLine();
		
		Object[][] convolutions = new Object[data.rows()][];
		for (int i = 0; i < convolutions.length; i++)
			convolutions[i] = new Object[] {
				labels.get(i),
				Convolutions.OWA(weights, data.getRow(i))
			};
		
		return convolutions;
	}
	
	private static double[] getWeightVector(int dim, double alpha) {
		double[] vector = new double[dim];
		vector[0] = Q(1.0 / (double) dim, alpha);
		
		for (int i = 1; i < vector.length; i++)
			vector[i] = Q((double) (i + 1) / (double) dim, alpha) - Q((double) i / (double) dim, alpha);
		
		return vector;
	}
	
	private static double Q(double x, double alpha) {
		return Math.exp(alpha * Math.log(x));
	}
	
	private static void normalizeConvolutions(Object[][] convolutions) throws IOException {
		double aMin = (double) convolutions[convolutions.length - 1][1];
		double aMax = (double) convolutions[0][1];
		double denominator = aMax - aMin;
		for (int i = 0; i < convolutions.length; i++) {
			double val = ((double) convolutions[i][1] - aMin) / denominator;
			convolutions[i][1] = val;
		}
	}
	
	private static void outputConvolutions(Object[][] convolutions, String message) throws IOException {
		writer.write(message);
		writer.newLine();
		
		for (int i = 0; i < convolutions.length; i++) {
			writer.write(convolutions[i][0] + "\t" + convolutions[i][1]);
			writer.newLine();
		}
		
		writer.newLine();
	}
}